﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SQLite;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using HslCommunication;
using HslCommunication.Profinet.Siemens;
using Newtonsoft.Json.Linq;
using static System.Windows.Forms.AxHost;

namespace PC_Touch.DataOP
{
    class PLCDBType
    {
        public int id { get; set; }
        public string parameterType { get; set; } = "";
        public int parameterId { get; set; }
        public string address { get; set; } = "";
        public int startByte { get; set; }
        public ushort byteLenght { get; set; }
        public string parameterValue { get; set; } = "";
    }
    public static class PLCHelper
    {
        public static SiemensS7Net siemensTcpNet = null;
        public static OperateResult siemensConnect = null;

        public static string OpenPLC(string plcIP,int port = 102)
        {
            string re = "PLC connection failed";
            siemensTcpNet = new SiemensS7Net(SiemensPLCS.S1200, plcIP)
            {
                ConnectTimeOut = 1000,
                Port = port
            };
            siemensConnect = siemensTcpNet.ConnectServer();
            re = siemensConnect.IsSuccess ? "PLC connection successful" : "PLC connection failed";
            return re;
        }
        public static bool ReadPLCIO(string boolAddress)
        {
            bool re = false;
            if (siemensConnect.IsSuccess)
            {
               re = siemensTcpNet.ReadBool(boolAddress).Content;
            }
            return re;
        }
        public static bool[,] BatchReadPLCIO(string address,ushort maxRegisters)//二维数组存储[字节，位] I3.2 = [3,2]
        {
            bool[,] ret = new bool[maxRegisters, 8];
            OperateResult<byte[]> readBytesResult = siemensTcpNet.Read(address, maxRegisters);
            if (readBytesResult.IsSuccess)
            {
                var bytesReadFromPLC = readBytesResult.Content;
                // 将字节数据转换为二维布尔数组
                for (int byteIndex = 0; byteIndex < bytesReadFromPLC.Length; byteIndex++)
                {
                    for (int bitIndex = 0; bitIndex < 8; bitIndex++)
                    {
                        ret[byteIndex, bitIndex] = (bytesReadFromPLC[byteIndex] & (1 << bitIndex)) != 0;
                    }
                }
            }
            return ret;
        }
        static EventListener listener = new EventListener();
        static List<CollectionManagement> collectionsList = new List<CollectionManagement>();
        public static void TriggerEventInit()
        {
            var eventList = listener.LoadEvents();
            ReadParameterTabel();
            foreach (var item in eventList)
            {
                if (item.isEnable)
                {
                    listener.SubscribeToEvent(item.eventName, OnTriggerChanged);//订阅
                    Task.Run(() => TriggerEventMonitorAsync(item.variableAddress, item.eventName, item.variableType));
                }
            }
        }
        public static async Task TriggerEventMonitorAsync(string triggerAddr,string eventName,string variableType) 
        {
            while (true) 
            {
                try
                {
                    switch (variableType)
                    {
                        case "String":
                            // listener.SetValue(eventName, siemensTcpNet.Read();
                            break;
                        case "Float":
                            listener.SetValue(eventName, (double)siemensTcpNet.ReadFloat(triggerAddr).Content);
                            break;
                        case "Short":
                            listener.SetValue(eventName, (double)siemensTcpNet.ReadUInt16(triggerAddr).Content);
                            break;
                        case "Bool":
                            listener.SetValue(eventName, siemensTcpNet.ReadBool(triggerAddr).Content ? 1 : 0);
                            break;
                        default:
                            break;
                    }
                }
                catch (Exception EX)
                {
                    GlobalVariable._ConsoleOutputService.Write(EX.Message+"  "+EX.StackTrace);
                }
                await Task.Delay(100); // 使用 Task.Delay 替代 Thread.Sleep
            }

        }
        public static void ReadParameterTabel() 
        {
            string connectionString = $"Data Source={AppDomain.CurrentDomain.BaseDirectory}DataBase\\app.db;Cache=Shared";
            var helper = new SQLiteHelper<CollectionManagement>(connectionString);
            Func<IDataRecord, CollectionManagement> map = record =>
                        new CollectionManagement
                        {
                            id = record.GetInt32(0),
                            createDataTime = record.GetString(1),
                            parameterName = record.GetString(2),
                            parameterAddress = record.GetString(3),
                            parameterType = record.GetString(4),
                            parameterLength = record.GetInt32(5),
                            factor = record.GetDouble(6),
                            unit = !record.IsDBNull(7) ? record.GetString(7) : "",
                            eventId = record.GetInt32(8),
                            eventName = !record.IsDBNull(9) ? record.GetString(9) : "",
                            triggerAddress = record.GetString(10),
                        };
            collectionsList = helper.Query("SELECT [CollectionManagement].id,[CollectionManagement].createDataTime,parameterName,parameterAddress,parameterType,parameterLength,factor,unit,EventManagement.id AS eventId,eventName,variableAddress FROM CollectionManagement INNER JOIN EventManagement ON CollectionManagement.eventId = EventManagement.id", map);
        }
        public static void OnTriggerChanged(string eventName, double value)
        {
            ReadParameterTabel();
            GlobalVariable._ConsoleOutputService.Write($"Event triggered: {eventName} changed to {value}");
            string traceParameter = "{";
            List<PLCDBType> types = new List<PLCDBType>();
            int i = 0;
            int startByteIndex = 0;
            foreach (var child in collectionsList)
            {
                switch (child.parameterType)
                {
                    case "String":
                        types.Add(new PLCDBType() { id = i, parameterId = child.id, parameterType = child.parameterType, address = child.parameterAddress, startByte = startByteIndex, byteLenght = (ushort)child.parameterLength });
                        startByteIndex += (ushort)child.parameterLength;
                        break;
                    case "Float":
                        types.Add(new PLCDBType() { id = i, parameterId = child.id, parameterType = child.parameterType, address = child.parameterAddress, startByte = startByteIndex, byteLenght = 4 });
                        startByteIndex += 4;
                        break;
                    case "Short":
                        types.Add(new PLCDBType() { id = i, parameterId = child.id, parameterType = child.parameterType, address = child.parameterAddress, startByte = startByteIndex, byteLenght = 2 });
                        startByteIndex += 2;
                        break;
                    case "Bool":
                        types.Add(new PLCDBType() { id = i, parameterId = child.id, parameterType = child.parameterType, address = child.parameterAddress, startByte = startByteIndex, byteLenght = 1 });
                        startByteIndex += 1;
                        break;
                    default:
                        break;
                }
                i++;
            }
            string[] stringAddress = types.ConvertAll(item => item.address).ToArray();
            ushort[] ushortsByteLenght = types.ConvertAll(item => item.byteLenght).ToArray();
            //OperateResult<byte[]> read = siemensTcpNet.Read(new string[] { "DB100.0", "DB100.20", "DB200.20", "DB200.80" }, new ushort[] { 500, 500, 500, 300 });
            OperateResult<byte[]> read = siemensTcpNet.Read(stringAddress, ushortsByteLenght);
            for (int j = 0; j < types.Count; j++)
            {
                byte[] bytes = read.Content.Skip(types[j].startByte).Take(types[j].byteLenght).ToArray();

                switch (types[j].parameterType)
                {
                    case "String":
                        string stringData = siemensTcpNet.ByteTransform.TransString(bytes, 0, bytes.Length, Encoding.ASCII);
                        stringData = stringData.Substring(2, stringData.Length > 1 ? (int)stringData.Substring(1, 1)[0] : 0);
                        types[j].parameterValue = stringData;
                        break;
                    case "Bool":
                        types[j].parameterValue = (((bytes[0] >> int.Parse(types[j].address.Split('.')[2]??"0")) & 1) == 1).ToString();
                        break;
                    case "Float":
                        types[j].parameterValue = siemensTcpNet.ByteTransform.TransSingle(bytes,0).ToString("F3");
                        break;
                    case "Short":
                        types[j].parameterValue = siemensTcpNet.ByteTransform.TransInt16(bytes, 0).ToString();
                        break;
                    default:
                        break;
                }
                GlobalVariable._ConsoleOutputService.Write($"Read current PLC data [{types[j].address}] = [{types[j].parameterValue}]");

            }
            string connectionString = $"Data Source={AppDomain.CurrentDomain.BaseDirectory}DataBase\\app.db;Cache=Shared";
            using (var connection = new SQLiteConnection(connectionString))
            {
                connection.Open();
                using (var transaction = connection.BeginTransaction())
                {
                    try
                    {
                        var values = new List<string>();
                        var parameters = new List<SQLiteParameter>();
                        for (int k = 0; k < types.Count; k++)
                        {
                            var item = types[k];
                            values.Add("(@parameterId" + k + ", @triggerValue" + k + ", @parameterValue" + k + ")");
                            parameters.Add(new SQLiteParameter("@parameterId" + k, item.parameterId));
                            parameters.Add(new SQLiteParameter("@triggerValue" + k, eventName));
                            parameters.Add(new SQLiteParameter("@parameterValue" + k, item.parameterValue));
                        }
                        using (var command = connection.CreateCommand())
                        {
                            command.Transaction = transaction;
                            command.CommandText = "INSERT INTO CollectionValue (parameterId, triggerValue,parameterValue) VALUES " + string.Join(", ", values);
                            command.Parameters.AddRange(parameters.ToArray());
                            command.ExecuteNonQuery();
                        }
                        transaction.Commit();
                    }
                    catch (Exception ex)
                    {
                        transaction.Rollback();
                        Console.WriteLine($"Error: {ex.Message}");
                    }
                }
            }
        }
    }

}

